//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Interfaces;
using LargoCommon.Localization;
using LargoCommon.Midi;
namespace LargoCommon.Music
{
///
/// Musical Track.
///
[Serializable]
public sealed class MusicalLine : IAbstractLine
{
#region Fields
////
//// Last Used Bit.
////
//// private int lastUsedBit;
/// Complete list of all tones in musical part.
private MusicalStrikeCollection tones;
///
/// Musical Block.
///
[NonSerialized]
private MusicalStrip strip;
/// Gets or sets corresponding MIDI track.
/// Property description.
private MidiEventCollection midiEventColl;
#endregion
#region Constructors
///
/// Initializes a new instance of the MusicalLine class.
///
public MusicalLine() {
this.tones = new MusicalStrikeCollection();
//// this.IsNested = false;
this.LineIdent = Guid.NewGuid();
this.StatusList = new List();
this.Purpose = LinePurpose.None;
this.FirstStatus = new LineStatus {
LocalPurpose = LinePurpose.None,
Instrument = new MusicalInstrument(MidiMelodicInstrument.None) //// FixedInstrument
};
this.MainVoice = new MusicalVoice {
Octave = this.FirstStatus.Octave,
Instrument = this.FirstStatus.Instrument,
Loudness = this.FirstStatus.Loudness,
Line = this
};
}
///
/// Initializes a new instance of the class.
///
/// The given status.
public MusicalLine(LineStatus givenStatus) {
this.Reset();
this.tones = new MusicalStrikeCollection();
//// this.IsNested = false;
this.LineIdent = Guid.NewGuid();
this.StatusList = new List();
this.FirstStatus = givenStatus ?? new LineStatus();
this.MainVoice = new MusicalVoice {
Octave = this.FirstStatus.Octave,
Instrument = this.FirstStatus.Instrument,
Loudness = this.FirstStatus.Loudness,
Line = this
};
//// this.Purpose = this.Purpose;
//// new MusicalInstrument(MidiMelodicInstrument.None); //// FixedInstrument
}
///
/// Initializes a new instance of the MusicalLine class.
///
/// The given midi track.
/// The given strip.
public MusicalLine(IMidiTrack givenMidiTrack, MusicalStrip givenStrip) /* 2019/01 MusicalSection givenArea */
{
Contract.Requires(givenMidiTrack != null);
this.Name = givenMidiTrack.Name;
this.TrackNumber = (byte)givenMidiTrack.TrackNumber;
this.LineIdent = Guid.NewGuid();
this.Strip = givenStrip;
this.StatusList = new List();
this.Purpose = LinePurpose.Fixed;
this.FirstStatus = new LineStatus {
Staff = givenMidiTrack.Staff,
Voice = givenMidiTrack.Voice,
LocalPurpose = LinePurpose.Fixed //// 2019/01 LinePurpose.Fixed
};
this.MainVoice = new MusicalVoice {
Octave = this.FirstStatus.Octave,
Instrument = this.FirstStatus.Instrument,
Loudness = this.FirstStatus.Loudness,
Line = this,
Channel = givenMidiTrack.Channel
};
MusicalLineType lineType = MusicalLineType.None;
if (givenMidiTrack.IsMelodic) {
lineType = MusicalLineType.Melodic;
this.FirstStatus.Instrument = new MusicalInstrument(givenMidiTrack.FirstMelodicInstrumentInEvents);
}
else
if (givenMidiTrack.IsRhythmical) {
lineType = MusicalLineType.Rhythmic;
this.FirstStatus.Instrument = new MusicalInstrument(givenMidiTrack.FirstRhythmicInstrumentInEvents);
}
this.FirstStatus.LineType = lineType;
var midiTones = new MidiTones(givenMidiTrack); /* 2019/01 givenArea */
if (midiTones.List.Count <= 0) {
return;
}
this.AppendMidiTrackTones(midiTones); //// midiTones
this.Purpose = LinePurpose.Fixed;
}
///
/// Initializes a new instance of the class.
///
/// The mark track.
/// The given header.
public MusicalLine(XElement markTrack, MusicalHeader givenHeader) {
Contract.Requires(markTrack != null);
if (markTrack == null) {
return;
}
byte rhythmicOrder = givenHeader.System.RhythmicOrder;
this.LineType = DataEnums.ReadAttributeMusicalLineType(markTrack.Attribute("LineType"));
this.Purpose = DataEnums.ReadAttributeLinePurpose(markTrack.Attribute("Purpose"));
this.LineIndex = XmlSupport.ReadByteAttribute(markTrack.Attribute("LineIndex"));
this.StatusList = new List();
this.FirstStatus = new LineStatus {
LocalPurpose = DataEnums.ReadAttributeLinePurpose(markTrack.Attribute("Purpose"))
};
var xstatusList = markTrack.Element("StatusList");
if (xstatusList != null) {
var xstatusListElements = xstatusList.Elements();
var xtrackStatuses = xstatusListElements.ToList();
var xtrackStatus1 = xtrackStatuses.FirstOrDefault();
if (xtrackStatus1 != null) {
this.FirstStatus.SetXElement(xtrackStatus1, givenHeader);
}
this.CurrentStatus = this.FirstStatus.Clone() as LineStatus;
foreach (var xtrackStatus in xtrackStatuses) {
//// var writtenStatus = new TrackStatus(xtrackStatus, givenHeader);
if (this.CurrentStatus == null) {
continue;
}
this.CurrentStatus.SetXElement(xtrackStatus, givenHeader);
var status = this.CurrentStatus.Clone() as LineStatus;
if (status == null) {
continue;
}
status.BarNumber = XmlSupport.ReadIntegerAttribute(xtrackStatus.Attribute("Bar"));
this.StatusList.Add(status);
}
var purpose = (from ts in this.StatusList where ts.LocalPurpose != LinePurpose.None select ts.LocalPurpose)
.FirstOrDefault();
this.Purpose = purpose;
}
this.LineIdent = Guid.NewGuid();
this.MainVoice = new MusicalVoice {
Octave = this.FirstStatus.Octave,
Instrument = this.FirstStatus.Instrument,
Loudness = this.FirstStatus.Loudness,
Line = this
};
var attribute = markTrack.Attribute("Channel");
if (attribute != null) {
this.MainVoice.Channel = (MidiChannel)XmlSupport.ReadByteAttribute(attribute);
}
var xtones = markTrack.Element("Tones");
if (xtones != null) {
var mts = new MusicalStrikeCollection(xtones, rhythmicOrder);
this.SetTones(mts);
}
}
#endregion
#region Properties - Xml
/// Gets Xml representation.
/// Property description.
public XElement GetXElement {
get {
var xtrack = new XElement("Track", null);
xtrack.Add(new XAttribute("LineIndex", this.LineIndex));
xtrack.Add(new XAttribute("LineType", this.LineType));
xtrack.Add(new XAttribute("Channel", (int)this.MainVoice.Channel));
xtrack.Add(new XAttribute("Purpose", this.Purpose));
//// xtrack.Add(new XAttribute("IsImported", this.IsImported));
//// xtrack.Add(this.FirstStatus.GetXElement);
//// Status
var statusList = this.StatusList;
if (statusList != null) {
XElement xstatusList = new XElement("StatusList");
LineStatus lastStatus = null;
foreach (var ts in statusList) {
var xstatus = lastStatus == null ? ts.GetXElement : ts.GetChangeXElement(lastStatus);
xstatusList.Add(xstatus);
lastStatus = ts;
}
xtrack.Add(xstatusList);
}
//// Music tones
XElement xrealTones = new XElement("Tones");
var mtones = this.Tones;
if (mtones != null) {
byte lastInstrument = (byte)MidiMelodicInstrument.None;
foreach (IMusicalTone mtone in mtones.Where(mtone => mtone != null)) {
var xelement = mtone.GetXElement;
if (mtone.InstrumentNumber != lastInstrument) { //// 2019/02 rt is MusicalStrike mtone &&
xelement.Add(new XAttribute("Instrument", mtone.InstrumentNumber));
lastInstrument = mtone.InstrumentNumber;
}
xrealTones.Add(xelement);
}
}
xtrack.Add(xrealTones);
return xtrack;
}
}
#endregion
#region Public Properties
///
/// Gets or sets the type of the line.
///
///
/// The type of the line.
///
public MusicalLineType LineType {
get {
if (this.StatusList == null || this.FirstStatus == null) {
return MusicalLineType.None;
}
return this.FirstStatus.LineType;
}
set {
if (this.StatusList != null && this.FirstStatus != null) {
this.FirstStatus.LineType = value;
}
}
}
/// Gets or sets purpose of the track.
/// Property description.
/// Returns value.
public LinePurpose Purpose { get; set; }
///
/// Gets or sets the track identifier.
///
///
/// The track identifier.
///
public Guid LineIdent { get; set; }
/// Gets or sets line index - i.e. an unique mark of the track in the musical model.
/// Property description.
/// Returns value.
public int LineIndex { get; set; }
///
/// Gets the line number.
///
///
/// The line number.
///
public byte LineNumber => (byte)(this.LineIndex + 1);
///
/// Gets or sets the current instrument.
///
///
/// The current instrument.
///
public byte CurrentInstrument { get; set; }
///
/// Gets a value indicating whether this instance has content.
///
///
/// true if this instance has content; otherwise, false.
///
[UsedImplicitly]
public bool HasContent => this.Purpose != LinePurpose.None && this.Purpose != LinePurpose.Mute;
///
/// Gets or sets the current status.
///
///
/// The current status.
///
public LineStatus FirstStatus {
get {
Contract.Ensures(Contract.Result() != null);
var firstStatus = this.StatusList.FirstOrDefault();
if (firstStatus == null) {
throw new InvalidOperationException("First status is null.");
}
return firstStatus;
}
set =>
this.StatusList = new List
{
value
};
}
///
/// Gets or sets the current status.
///
///
/// The current status.
///
public LineStatus CurrentStatus { get; set; }
///
/// Gets or sets the status list.
///
///
/// The status list.
///
public List StatusList { get; set; }
///
/// Gets or sets track engine MusicalTrackEngine.
///
/// Property description.
public object TrackEngine { get; set; }
///
/// Gets or sets the name.
///
///
/// The track name.
///
public string Name { get; set; }
/// Gets or sets track number - original midi-track number.
/// Property description.
/// Returns value.
public byte TrackNumber { [UsedImplicitly] get; set; }
///
/// Gets corresponding MIDI track.
///
///
/// Property description.
///
public MidiEventCollection MidiEventCollection {
get {
if (this.midiEventColl == null || this.midiEventColl.Channel != this.MainVoice.Channel) {
this.midiEventColl = new MidiEventCollection(this.MainVoice.Channel);
}
return this.midiEventColl;
}
}
/// Gets a value indicating whether of empty musical part.
/// Property description.
/// Returns value.
public bool IsEmpty {
get {
if (this.Tones == null || this.Tones.Count == 0) {
return true;
}
var realTones = from t in this.Tones where t.ToneType != MusicalToneType.Empty && !t.IsEmpty select 1;
return !realTones.Any();
}
}
///
/// Gets or sets musical block.
///
/// Property description.
public MusicalStrip Strip {
get {
Contract.Ensures(Contract.Result() != null);
if (this.strip == null) {
throw new InvalidOperationException("Musical strip is null.");
}
return this.strip;
}
set => this.strip = value ?? throw new ArgumentException(LocalizedMusic.String("Argument cannot be null."), nameof(value));
}
///
/// Gets or sets a value indicating whether this instance is selected.
///
///
/// true if this instance is selected; otherwise, false.
///
public bool IsSelected { get; set; }
///
/// Gets complete list of all tones in musical part.
///
///
/// Property description.
///
[XmlIgnore]
public MusicalStrikeCollection Tones {
get {
Contract.Ensures(Contract.Result() != null);
return this.tones ?? (this.tones = new MusicalStrikeCollection());
}
}
#endregion
/// Gets or sets the system length.
/// The length of the system.
public int SystemLength { get; set; }
/// Gets or sets the main voice.
/// The main voice.
public IAbstractVoice MainVoice { get; set; }
/// Gets or sets the voices.
/// The voices.
public List Voices {
get => new List {
this.MainVoice
};
set {
}
}
///
/// Gets a list of identifiers.
///
///
/// A list of identifiers.
///
public IList Identifiers {
get {
var items = new List();
var item = new KeyValuePair("Line type", this.LineType.ToString());
items.Add(item);
item = new KeyValuePair("Line number", this.LineNumber.ToString());
items.Add(item);
item = new KeyValuePair("Purpose", this.Purpose.ToString());
items.Add(item);
item = new KeyValuePair("Octave", this.MainVoice.Octave.ToString());
items.Add(item);
item = new KeyValuePair("Loudness", this.MainVoice.Loudness.ToString());
items.Add(item);
item = new KeyValuePair("Rhythmic tension", this.FirstStatus.RhythmicTension.ToString());
items.Add(item);
item = new KeyValuePair(LocalizedMusic.String("Channel"), this.MainVoice.Channel.ToString());
items.Add(item);
item = new KeyValuePair("Instrument", this.MainVoice.Instrument.ToString());
items.Add(item);
return items;
}
}
#region Tones
/// Gets total number of not empty tones in this part.
/// Property description.
/// Returns value.
[UsedImplicitly]
public int NumberOfTones {
get {
//// .Cast()
var num = (from tone in this.Tones
where tone != null //// && melTone.Pitch != null
select 1)
.Count();
return num;
}
}
/// Gets total number of not empty tones in this part.
/// Property description.
/// Returns value.
[UsedImplicitly]
public long DurationOfTones {
get {
//// .Cast()
long num = (from melTone in this.Tones
where melTone != null //// && melTone.Pitch != null
select melTone.Duration)
.Sum();
return num;
}
}
///
/// Gets the number of tones.
///
/// Property description.
[UsedImplicitly]
public int NumberOfAllTones => this.Tones.Count;
#endregion
#region Tone history
/// Gets or sets current melodic tone.
/// Property description.
/// [XmlIgnore]
public MusicalTone CurrentTone { get; set; }
/// Gets or sets last melodic tone.
/// Property description.
/// [XmlIgnore]
public MusicalTone LastTone { get; set; }
/// Gets or sets last but one melodic tone.
/// Property description.
/// [XmlIgnore]
public MusicalTone PenultTone { get; set; }
#endregion
#region Meta Texts
///
/// Gets Midi Meta Text.
///
/// General musical property.
/// Returns value.
public string MidiMetaText {
get {
var s = "*** TRACK ***" + this.LineIndex.ToString(CultureInfo.CurrentCulture);
//// if (this.LineIndex == 1) { //// if (this.StatusChanged && this.Status != null) {
//// s = this.AppendMetaTrackProperties(s);
//// }
return s;
}
}
#endregion
#region Private Properties
/// Gets or sets file name.
/// Property description.
/// Returns value.
private byte Multiplicity { get; set; }
#endregion
#region Static factory methods
///
/// Gets the new musical track.
///
/// Type of the line.
/// The musical block.
///
/// Returns value.
///
public static MusicalLine GetNewMusicalLine(MusicalLineType lineType, MusicalBlock musicalBlock) {
var firstStatus = new LineStatus() {
LocalPurpose = LinePurpose.None,
LineType = lineType //// channel == MidiChannel.DrumChannel ? MusicalLineType.Rhythmic : MusicalLineType.Melodic
};
var track = new MusicalLine(firstStatus) {
Strip = musicalBlock.Strip,
Purpose = LinePurpose.None
};
return track;
}
#endregion
#region Public Methods
///
/// Sets the tones.
///
/// The given tones.
public void SetTones(MusicalStrikeCollection givenTones) {
this.tones = givenTones;
}
///
/// Sets the tones.
///
/// The given tones.
/// The given bar number.
[UsedImplicitly]
public void SetTones(MusicalStrikeCollection givenTones, int givenBarNumber) {
this.tones = givenTones;
foreach (var tone in this.tones) {
tone.BarNumber = givenBarNumber;
}
}
///
/// Sets the midi event collection.
///
/// The collection.
public void SetMidiEventCollection(MidiEventCollection collection) {
this.midiEventColl = collection;
}
///
/// Reset musical track.
///
/// The musical strip.
/// Clear Tones.
public void Reset(MusicalStrip musicalStrip, bool clearTones) { //// MusicalRules musicalRules,
this.Strip = musicalStrip;
//// this.tones = new MusicalStrikeCollection();
this.FirstStatus.MelodicPlan = new MelodicPlan();
if (clearTones) {
this.Reset();
}
}
/// Makes a deep copy of the MusicalLine object.
/// Returns object.
public object Clone() {
//// this.LineIndex/0
var status = (LineStatus)this.FirstStatus.Clone();
var track = new MusicalLine(status) {
Name = this.Name,
LineIndex = this.LineIndex,
TrackNumber = this.TrackNumber,
Strip = this.Strip,
Multiplicity = this.Multiplicity,
TrackEngine = this.TrackEngine
};
track.Reset();
return track;
}
///
/// Clones the specified include tones.
///
/// If set to true [include tones].
/// Returns value.
public MusicalLine Clone(bool includeTones) {
var track = (MusicalLine)this.Clone();
if (includeTones) {
track.Tones.AddCollection(this.Tones.Clone(false), false);
}
return track;
}
///
/// Resets this instance.
///
public void Reset() {
this.tones = new MusicalStrikeCollection();
}
#endregion
#region Public Methods - Characteristics
///
/// Quotients the of occupation.
///
/// Returns value.
public float QuotientOfOccupation() {
var cnt = 0;
var sum = 0;
var header = this.Strip.Context.Header;
for (var barNumber = 1; barNumber <= header.NumberOfBars; barNumber++) {
var occupation = this.OccupationOfBar(header.System.RhythmicOrder, barNumber);
var barSum = (from v in occupation where v > 0 select v).Sum();
var barCount = (from v in occupation where v > 0 select v).Count();
sum += barSum;
cnt += barCount;
}
var value = ((float)sum) / cnt;
return value;
}
/// Returns number of tones with given musical pitch.
/// Musical pitch.
/// Returns value.
[UsedImplicitly]
public byte CountOfUsedPitch(MusicalPitch pitch) {
Contract.Requires(pitch != null);
var num = (from melTone in this.Tones
.Cast()
where melTone.Pitch != null && melTone.Pitch.IsEqualTo(pitch)
select 1)
.Count();
return (byte)num;
}
#endregion
#region String representation
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
s.AppendFormat(CultureInfo.CurrentCulture, "{0,4}, ", this.LineIndex); //\r\n\
s.Append(this.FirstStatus.LineType);
//// s.Append(this.OctaveString);
//// s.Append(this.BandTypeString);
//// s.Append(this.InstrumentString);
//// if (this.MusicalTones == null) { return s.ToString(); }
var firstTone = this.Tones.FirstOrDefault();
if (firstTone != null) {
s.Append(string.Format(CultureInfo.InvariantCulture, "#Bar{0}#", firstTone.BarNumber));
}
s.AppendLine(string.Empty);
s.Append(this.Tones.TonesToString());
s.AppendFormat("Ident={0}", this.LineIdent);
//// this.TonesToString(s);
return s.ToString();
}
#endregion
#region Public Methods - Load from Midi Lines
///
/// Append MidiTrack Tones.
///
/// The midi tones.
public void AppendMidiTrackTones(IMidiTones midiTones) {
if (midiTones == null) {
return;
}
this.FirstStatus.LineType = midiTones.IsRhythmical ? MusicalLineType.Rhythmic : MusicalLineType.Melodic;
//// musicalTrack.Instrument = midiTrack.Instrument; musicalTrack.Channel = midiTrack.Channel;
//// Collection instrument = midiTrack.ReadRhythmicInstruments();
//// if (instrument != null) { musicalTrack.Instrument = (byte)instrument.FirstOrDefault(); }
//// this.IsImported = true;
this.Name = midiTones.Name;
this.FirstStatus.Octave = midiTones.Octave;
this.FirstStatus.BandType = midiTones.BandType; //// MusicalPitch.BandTypeFromOctave(musicalTrack.MusicalOctave);
if (this.Strip.Context.Header.Metric.MetricGround > 0) { //// && midiTrack.Sequence != null
var ts = MusicalStrikeCollection.GetTones(this.Strip.Context.Header, midiTones, this.FirstStatus.IsMelodic);
this.SetTones(ts);
}
}
#endregion
#region Public Methods - Transformation
///
/// Total Horizontal Inversion.
///
public void TotalHorizontalInversion() {
if (this.Tones == null || this.Tones.Count == 0) {
return;
}
var mtc = new MusicalStrikeCollection();
var lastMusTone = this.Tones.LastOrDefault();
if (lastMusTone != null) {
var extraBarNumber = lastMusTone.BarNumber + 1;
foreach (var mtone in this.Tones) {
if (!(mtone is MusicalStrike tone)) {
continue;
}
tone.BarNumber = extraBarNumber - tone.BarNumber;
var p = tone.IsFromPreviousBar;
tone.IsFromPreviousBar = tone.IsGoingToNextBar;
tone.IsGoingToNextBar = p;
mtc.Insert(0, tone);
}
}
this.tones = mtc;
}
///
/// Bar Horizontal Inversion.
///
public void BarHorizontalInversion() {
if (this.Tones == null || this.Tones.Count == 0) {
return;
}
var mtc = new MusicalStrikeCollection();
var lastMusTone = this.Tones.LastOrDefault();
if (lastMusTone == null) {
return;
}
var lastBarNumber = lastMusTone.BarNumber;
for (var barNum = 1; barNum <= lastBarNumber; barNum++) {
var barTones = this.MusicalTonesInBar(barNum);
switch (barTones.Count) {
case 0:
continue;
case 1:
if (barTones[0] is MusicalStrike mtone) {
mtone.IsGoingToNextBar = false;
mtone.IsFromPreviousBar = false;
mtc.Add(mtone);
}
continue;
}
DoInversion(barTones, mtc);
}
this.tones = mtc;
}
///
/// Divides the tones.
///
/// The factor number.
[UsedImplicitly]
public void DivideTones(byte factorNumber) {
var list = this.Tones.ToList();
//// Introduce foreach action - DefExpress
foreach (var tn in list) {
var tone = tn as MusicalStrike;
if (tone != null && (tone.IsPause || tone.Duration % factorNumber != 0)) {
continue;
}
if (tone != null) {
var d = (byte)(tone.BitRange.Length / factorNumber);
tone.BitRange = new BitRange(tone.BitRange.Order, tone.BitRange.BitFrom, d);
tone.Duration = d;
MusicalStrike newTone = null;
for (var i = 1; i < factorNumber; i++) {
newTone = (MusicalStrike)tone.CloneTone();
newTone.IsGoingToNextBar = false;
newTone.IsFromPreviousBar = false;
newTone.BitRange = new BitRange(tone.BitRange.Order, (byte)(tone.BitRange.BitFrom + (i * d)), d);
this.Tones.Add(newTone);
}
if (newTone != null) {
newTone.IsGoingToNextBar = tone.IsGoingToNextBar;
}
}
if (tone != null) {
tone.IsGoingToNextBar = false;
}
}
var sortedTones = from mt in this.Tones
orderby mt.BitPosition
select mt;
this.tones = new MusicalStrikeCollection(sortedTones.ToList());
}
#endregion
#region Public Methods - Manipulation with tones
/// Gets or sets previous melodic tone.
/// Property description.
/// Melodic tone.
/// Returns value.
[UsedImplicitly]
public MusicalTone PreviousTone(MusicalStrike givenTone) {
Contract.Requires(givenTone != null);
MusicalTone tone = null;
//// if (givenTone == null) { return false; }
var idx = givenTone.OrdinalIndex; //// this.MusicalTones.IndexOf(givenTone);
if (idx <= 0) {
return null;
}
for (var i = idx - 1; i >= 0; i--) {
if (i >= this.Tones.Count) {
continue;
}
tone = this.Tones[i] as MusicalTone;
if (tone != null && tone.IsTrueTone) { //// Loudness > 0
return tone; //// Avoid multiple or conditional return statements.
}
}
return tone;
}
/// List of tones in one bar of musical part.
/// Musical area.
/// Returns value.
public MusicalStrikeCollection MusicalTonesInArea(MusicalSection givenArea) {
Contract.Ensures(Contract.Result() != null);
var list = from mT in this.Tones
where mT.BarNumber >= givenArea.BarFrom && mT.BarNumber <= givenArea.BarTo
select mT;
var mtc = new MusicalStrikeCollection(list.ToList());
//// 2015/01
var newTone = (from mT in this.Tones
where mT.BarNumber == givenArea.BarTo + 1 && mT.IsFromPreviousBar
select mT).FirstOrDefault();
if (newTone != null) {
mtc.AddTone(newTone, true);
}
return mtc;
}
/// List of tones in one bar of musical part.
/// Number of musical bar.
/// Returns value.
public MusicalStrikeCollection MusicalTonesInBar(int barNumber) {
Contract.Ensures(Contract.Result() != null);
var list = from mT in this.Tones
where mT.BarNumber == barNumber
select mT;
var mtc = new MusicalStrikeCollection(list.ToList());
return mtc;
}
/// List of tones in one bar of musical part.
/// Number of musical bar.
/// Returns value.
public MusicalToneCollection MelodicTonesInBar(int barNumber) {
var list = from mT in this.Tones
where mT.BarNumber == barNumber && mT.ToneType == MusicalToneType.Melodic
select mT;
var mtc = new MusicalToneCollection();
foreach (var mtone in list.OfType().Where(mtone => !mtone.IsEmpty)) {
mtc.Add(mtone);
}
return mtc;
}
///
/// Returns musical tone at given bar and tick in this part.
///
/// Number of musical bar.
/// Rhythmical tick.
/// The rhythmic structure.
///
/// Returns value.
///
[UsedImplicitly]
public MusicalStrike MusicalToneAt(int barNumber, byte tick, FiguralStructure rhythmicStructure) { //// RhythmicStructure
var list = this.MusicalTonesInBar(barNumber);
if (rhythmicStructure == null) {
return null;
}
var level = rhythmicStructure.LevelOfBit(tick);
return (level < list.Count ? list[level] : null) as MusicalStrike;
}
/// Add on musical tone to the end of part.
/// Musical tone.
public void AddMusicalTone(MusicalStrike givenTone) {
Contract.Requires(givenTone != null);
//// if (givenTone == null) { return false; }
givenTone.OrdinalIndex = this.Tones.Count;
this.Tones.Add(givenTone);
}
#endregion
#region Private static methods
///
/// Does the inversion.
///
/// The bar tones.
/// The tones.
private static void DoInversion(IList barTones, ICollection tones) {
Contract.Requires(barTones != null);
Contract.Requires(tones != null);
byte bit = 0;
IMusicalTone mtoneA = null, mtoneB = null;
var tonePosition = barTones.Count;
while (--tonePosition >= 0) {
var mtone0 = barTones[tonePosition];
if (mtone0 == null) {
continue;
}
if (mtone0.IsPause && tonePosition > 0) {
var mtone1 = mtone0;
mtone0 = barTones[tonePosition - 1];
//// swap tones followed by a short pause
if (mtone0 != null) {
if (!mtone0.IsPause && mtone1.IsPause && mtone0.Duration > mtone1.Duration) {
mtoneA = mtone0;
mtoneB = mtone1;
}
else {
mtoneA = mtone1;
mtoneB = mtone0;
}
}
if (mtoneA != null) {
mtoneA.BitFrom = bit;
mtoneA.IsGoingToNextBar = false;
mtoneA.IsFromPreviousBar = false;
//// mtoneA.BitTo = (byte)(bit + mtoneA.Duration - 1);
bit = (byte)(mtoneA.BitTo + 1);
tones.Add(mtoneA);
mtoneB.BitFrom = bit;
mtoneB.IsGoingToNextBar = false;
mtoneB.IsFromPreviousBar = false;
//// mtoneB.BitTo = (byte)(bit + mtoneB.Duration - 1);
bit = (byte)(mtoneB.BitTo + 1);
tones.Add(mtoneB);
}
tonePosition--;
}
else {
mtone0.BitFrom = bit;
mtone0.IsGoingToNextBar = false;
mtone0.IsFromPreviousBar = false;
//// mtone0.BitTo = (byte)(bit + mtone0.Duration - 1);
bit = (byte)(mtone0.BitTo + 1);
tones.Add(mtone0);
}
}
}
#endregion
#region Private methods
///
/// Occupations the of bar.
///
/// The rhythmic order.
/// The number of bar.
///
/// Returns value.
///
private int[] OccupationOfBar(byte rhythmicOrder, int barNumber) {
var occupation = new int[rhythmicOrder];
for (byte tick = 0; tick < rhythmicOrder; tick++) {
occupation[tick] = 0;
}
var tonesInBar = this.MusicalTonesInBar(barNumber);
foreach (var tone in tonesInBar) {
for (var tick = tone.BitFrom; tick <= tone.BitTo; tick++) {
occupation[tick] += 1;
}
}
return occupation;
}
#endregion
}
}